﻿using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Reflection;
using System.Security.Principal;
using System.Text;
using System.Web;
using Ensynch.FIM;
using Microsoft.ReportingServices.DataProcessing;


namespace FimDataProcessingExtension
{
    public sealed class FimCommand : IDbCommand, IDbCommandAnalysis
    {
        // Create a logger for use in this class.
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(
                System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        #region Variables

        private FimConnection fimConnection;
        private FimTransaction fimTransaction;

        #endregion

        #region Constructors

        public FimCommand()
        {
            // Implement the default constructor here
        }

        // Implement other constructors here
        public FimCommand(FimConnection fimConnection)
        {
            this.fimConnection = fimConnection;
        }

        public FimCommand(string commandText)
        {
            this.commandText = commandText;
        }

        public FimCommand(string commandText, FimConnection fimConnection)
        {
            this.commandText = commandText;
            this.fimConnection = fimConnection;
        }

        public FimCommand(string commandText, FimConnection fimConnection, FimTransaction fimTransaction)
        {
            this.commandText = commandText;
            this.fimConnection = fimConnection;
            this.fimTransaction = fimTransaction;
        }

        #endregion

        #region Properties

        private FimQuery query;
        private FimQuery Query
        {
            get
            {
                if (query == null)
                {
                    if (commandText == null)
                    {
                        throw new ApplicationException("Attempt to access Query before commandText was set.");
                    }
                    if (fimConnection == null)
                    {
                        throw new ApplicationException("Attempt to access Query before fimConnection was set.");
                    }
                    query = new FimQuery(commandText, fimConnection.Client, fimConnection.ConnectionUser);
                }
                return query;
            }
        }

        #endregion

        #region IDbCommandAnalysis Members

        /// <summary>
        /// This one has to parse out all parameters from the query, so the dataset preview can list them.
        /// </summary>
        IDataParameterCollection IDbCommandAnalysis.GetParameters()
        {
            try
            {
                FimTools.LogTrace(false);

                FimDataParameterCollection collection = new FimDataParameterCollection();
                collection.AddAll(Query.Parameters);

                StringBuilder msg = new StringBuilder();
                foreach (IDataParameter param in collection)
                {
                    msg.AppendLine(param.ParameterName + " = " + param.Value);
                }
                log.Debug("[GetParameters]\r\n" + msg.ToString());

                return collection;
            }
            catch (Exception exc)
            {
                log.Error("[IDbCommandAnalysis.GetParameters]", exc);
                throw;
            }
        }

        #endregion

        #region IDbCommand Members

        private FimDataParameterCollection parameters = new FimDataParameterCollection();
        /// <summary>
        /// This one is used by the server to access and build up the parameterCollection.
        /// </summary>
        IDataParameterCollection IDbCommand.Parameters
        {
            get
            {
                return parameters;
            }
        }

        private String commandText;
        string IDbCommand.CommandText
        {
            get
            {
                FimTools.LogTrace(false);

                return commandText;
            }
            set
            {
                FimTools.LogTrace(false);

                commandText = value;
            }
        }

        //Gets or sets the wait time before terminating the attempt to execute a command and generating an error.
        int IDbCommand.CommandTimeout
        {
            /*
            * The sample does not support a command time-out. 
            */
            get
            {
                return 0;
            }
            set
            {
            }
        }

        /// <summary>
        /// Gets or Sets the Command type. For FIM Commands only a text type with a XPath filter is acceptable.
        /// </summary>
        /// <value>The XPath filter of which objects to select.</value>
        /// <returns>A text string that contains the XPath filter set by the caller</returns>
        CommandType IDbCommand.CommandType
        {
            /*
            * The sample only supports CommandType.Text.
            */
            get { return CommandType.Text; }
            set { if (value != CommandType.Text) throw new NotSupportedException(); }
        }
        
        IDbTransaction IDbCommand.Transaction
        {
            /*
            * Transactions are not supported with Active Directory.
            */
            get 
            {
                throw new NotSupportedException();
            }
            set 
            {
                throw new NotSupportedException();
            }
        }
        
        /****
        * IMPLEMENT THE REQUIRED METHODS.
        ****/
        void IDbCommand.Cancel()
        {
            // The sample does not support canceling a command
            // once it has been initiated.
            throw new NotSupportedException();
        }

        //Creates a new instance of an IDataParameter object. Here it is ADDataParameter.
        IDataParameter IDbCommand.CreateParameter()
        {
            try
            {
                FimTools.LogTrace(false);

                return (IDataParameter)(new FimDataParameter());
            }
            catch (Exception exc)
            {
                log.Error("[IDbCommand.CreateParameter]", exc);
                throw;
            }
        }

        IDataParameter CreateParameter(string parameterName, object value)
        {
            try
            {
                FimTools.LogTrace(false);

                FimDataParameter oParam = new FimDataParameter(parameterName, value);
                return (IDataParameter)(oParam);
            }
            catch (Exception exc)
            {
                log.Error("[CreateParameter]", exc);
                throw;
            }
        }

        //Executes the CommandText against the Connection and builds an IDataReader.
        IDataReader IDbCommand.ExecuteReader(CommandBehavior behavior)
        {
            try
            {
                FimTools.LogTrace(false);

                if (fimConnection == null)
                {
                    throw new Exception("FimCommand.ExecuteReader() does not have a valid connection");
                }

                DateTime start = DateTime.Now;
                DateTime finish;
                StringBuilder buffer;
                TimeSpan elapsed;

                // Remember that the user is only impersonated during the call to FimConnection.Open().
                // Note that the FimDataReader will call FimConnection.MaybeImpersonate() when necessary.
                Query.Parameters = ((FimDataParameterCollection)((IDbCommand)this).Parameters).ToDictionary();
                FimDataReader reader = new FimDataReader(Query, fimConnection);
                finish = DateTime.Now;

                buffer = new StringBuilder();
                buffer.AppendLine("[IDbCommand.ExecuteReader] Successfully completed.");
                elapsed = finish - start;
                buffer.AppendLine("Total elapsed time: " + elapsed.ToString());
                log.Debug(buffer.ToString());

                return reader;
            }
            catch (Exception exc)
            {
                log.Error("[IDbCommand.ExecuteReader]", exc);
                throw;
            }
        }

        #endregion

        #region Dispose
        void IDisposable.Dispose()
        {
            /*
            * Dispose of the object and perform any cleanup.
            */
            // TODO: Dispose implementation
        }
        #endregion
    }
}

